package com.tgr.shortLink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.tgr.shortLink.admin.common.context.BaseContext;
import com.tgr.shortLink.admin.common.convention.errorCode.BaseErrorCode;
import com.tgr.shortLink.admin.common.convention.exception.ClientException;
import com.tgr.shortLink.admin.common.properties.JwtProperties;
import com.tgr.shortLink.admin.common.utils.JwtUtil;
import com.tgr.shortLink.admin.dao.entity.UserDO;
import com.tgr.shortLink.admin.dao.mapper.UserMapper;
import com.tgr.shortLink.admin.dto.UserInfoDTO;
import com.tgr.shortLink.admin.dto.req.UserLoginReqDTO;
import com.tgr.shortLink.admin.dto.req.UserRegisterRepDTO;
import com.tgr.shortLink.admin.dto.req.UserUpdateRepDTO;
import com.tgr.shortLink.admin.dto.resp.UserLoginRespDTO;
import com.tgr.shortLink.admin.dto.resp.UserRespDTO;
import com.tgr.shortLink.admin.service.GroupService;
import com.tgr.shortLink.admin.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.tgr.shortLink.admin.common.constant.RedisCacheConstant.LOCK_USER_REGISTER_KEY;
import static com.tgr.shortLink.admin.common.convention.errorCode.BaseErrorCode.*;


/**
 * 描述：用户接口实现层
 * 作者：小陶不慌张
 * 文件：UserServiceImpl
 * 日期：2023/12/17 16:04
 */

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService {

    private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;
    private final RedissonClient redissonClient;
    private final StringRedisTemplate stringRedisTemplate;
    private final JwtProperties jwtProperties;
    private final GroupService groupService;

    @Override
    public UserRespDTO getUserByUsername(String username) {
        LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getUsername, username);
        UserDO userDO = baseMapper.selectOne(queryWrapper);
        UserRespDTO result =new UserRespDTO();
        BeanUtils.copyProperties(userDO,result);
        return result;
    }

    @Override
    public Boolean hasUsername(String username) {
        return !userRegisterCachePenetrationBloomFilter.contains(username);
    }

    /**
     * 用户注册
     * @param requestParam 用户注册请求参数
     */
    @Override
    public void register(UserRegisterRepDTO requestParam) {
        if(!hasUsername(requestParam.getUsername())){
            throw new ClientException(USER_NAME_EXIST_ERROR);
        }
        //分布式锁
        RLock lock =redissonClient.getLock(LOCK_USER_REGISTER_KEY+requestParam.getUsername());
        try{
            if(lock.tryLock()){
                int inserted = baseMapper.insert(BeanUtil.toBean(requestParam,UserDO.class));
                if(inserted<1){
                    throw new ClientException(USER_SAVE_ERROR);
                }
                //将数据加入布隆过滤器
                userRegisterCachePenetrationBloomFilter.add(requestParam.getUsername());
                //用户注册时创建默认分组的流程
                groupService.saveGroup(requestParam.getUsername(),"默认分组");
                return;
            }
            throw new ClientException(USER_SAVE_ERROR);
        }finally {
            lock.unlock();
        }
    }

    /**
     * 用户信息更新
     * @param requestParam 更新请求实体
     */
    @Override
    public void update(UserUpdateRepDTO requestParam) {
        // 验证当前用户名是否为登录用户
        LambdaUpdateWrapper<UserDO> updateWrapper = Wrappers.lambdaUpdate(UserDO.class)
                .eq(UserDO::getUsername,BaseContext.getUsername());
        //BeanUtil类的toBean()方法用于将一个对象或Map转换成指定类型的JavaBean对象。
        baseMapper.update(BeanUtil.toBean(requestParam, UserDO.class), updateWrapper);
    }

    /**
     * 用户登录
     * @param requestParam 用户登录请求参数
     * @return 用户名及token
     */
    @Override
    public UserLoginRespDTO login(UserLoginReqDTO requestParam) {
        String username = requestParam.getUsername();
        String password = requestParam.getPassword();

        LambdaQueryWrapper<UserDO> loginWrapper = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getUsername,requestParam.getUsername())
                .eq(UserDO::getPassword,requestParam.getPassword())
                .eq(UserDO::getDelFlag,0);
        UserDO userDO = baseMapper.selectOne(loginWrapper);

        if(userDO == null){
            throw new ClientException(USER_NOT_EXIST_ERROR);
        }

        if(!password.equals(userDO.getPassword())){
            throw new ClientException(PASSWORD_VERIFY_ERROR);
        }
        //检查redis中该用户是否已有缓存信息
        Map<Object,Object> hasLoginMap = stringRedisTemplate.opsForHash().entries("login_"+requestParam.getUsername());
        // 如果用户重复登录，则返回token，实现多端登录
        if(CollUtil.isNotEmpty(hasLoginMap)){
            String token = hasLoginMap.keySet().stream()
                    .findFirst()
                    .map(Object::toString)
                    .orElseThrow(()->new ClientException("用户登录错误"));
            return new UserLoginRespDTO(token);
        }

        UserInfoDTO userInfo = UserInfoDTO.builder()
                .userId(String.valueOf(userDO.getId()))
                .username(userDO.getUsername())
                .realName(userDO.getRealName())
                .build();

         //第一步，生成JWT并返回给前端
        String token = JwtUtil.createJWT(
                // TODO jwtProperties为null
                //jwtProperties.getUserSecretKey(),
                "tgrShort",
                3000000,
                //jwtProperties.getUserTtl(),
                userInfo);

        /*
          Hash
          Key: Login_用户名
          Value:
           Key:token标识
           Val:JSON 字符串（用户信息）
         */
        //将Hash信息存入redis中
        stringRedisTemplate.opsForHash().put("login_"+requestParam.getUsername(), token, JSON.toJSONString(userDO));
        //设置Key过期时间
        stringRedisTemplate.expire("login_"+ requestParam.getUsername(),30L,TimeUnit.MINUTES);

        // 将登录信息返回json
        UserLoginRespDTO loginInfo = UserLoginRespDTO.builder()
                //.username(userLoginRespDTO.getUsername())
                .token(token)
                .build();

        return loginInfo;
    }


    public Boolean checkLogin(){
        return stringRedisTemplate.opsForHash().get("login_"+ BaseContext.getUsername(),BaseContext.getToken())!=null;
    }

    /**
     * 退出登录
     */
    @Override
    public void logout() {
        if(checkLogin()){
            stringRedisTemplate.delete("login_"+BaseContext.getUsername());
            // TODO 解决用户退出登录后token未过期，依然可以访问接口
            return;
        }
        throw new ClientException("用户Token不存在或用户未登录");
    }


}
